package com.zx.template.controller.wx;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.IpKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import com.zx.template.base.R;
import com.zx.template.base.ValidationGroup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/wxPay")
@Slf4j
public class WxPayController extends  AbstractWxPayApiController{
    @Autowired
    WxPayBean wxPayBean;

    private String notifyUrl;
    private static final String USER_PAYING = "USERPAYING";
    
    /**
     * wx-return_code
     */
    private static final String WX_RETURN_CODE = "return_code";
    /**
     * return_msg
     */
    private static final String WX_RETURN_MSG = "return_msg";
    private static final String WX_RESULT_CODE = "result_code";
    /**
     * wx-下单接口，出参prepay_id
     */
    private static final String WX_PREPAY_ID = "prepay_id";
    /**
     * wx-下单接口，出参 package
     */
    private static final String WX_PACKAGE = "package";

    private static final String WX_PAY_ERROR_PREFIX = "微信支付异常:";


    @Override
    public WxPayApiConfig getApiConfig() {
        WxPayApiConfig apiConfig;

        try {
            apiConfig = WxPayApiConfigKit.getApiConfig(wxPayBean.getAppId());
        } catch (Exception e) {
            apiConfig = WxPayApiConfig.builder()
                    .appId(wxPayBean.getAppId())
                    .mchId(wxPayBean.getMchId())
                    .partnerKey(wxPayBean.getPartnerKey())
                    .certPath(wxPayBean.getCertPath())
                    .domain(wxPayBean.getDomain())
                    .build();
        }
        notifyUrl = apiConfig.getDomain().concat("/wxPay/payNotify");
        return apiConfig;
    }

    /**
     * 微信小程序支付
     * 入参信息 参考 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1
     */
    @PostMapping(value = "/miniAppPay")
    @Transactional(rollbackFor = Exception.class)
    public R<WxPayPrepayVo> miniAppPay(@RequestBody @Validated(ValidationGroup.Add.class) PayDto dto,
                                          HttpServletRequest request) {
        // 校验 前端传的金额是否正确
        // checkOrderMoneyRight(dto);

        //todo 需要通过授权来获取openId(注意此 openId 和 小程序appid 要对应）（否则下面将抛出异常）
        String openId = "obFyW5PDWulcTEkWzFKLTPERu64g";

        String ip = IpKit.getRealIp(request);
        if (StrUtil.isBlank(ip)) {
            ip = "127.0.0.1";
        }

        String outTradeNo = WxPayKit.generateStr();
        Integer totalFee = changeY2F(dto.getRealPrice());
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        UnifiedOrderModel model = UnifiedOrderModel.builder().appid(wxPayApiConfig.getAppId()).mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr()).body(dto.getBody()).attach(dto.getAttach()).out_trade_no(outTradeNo)
                .total_fee(totalFee.toString()).spbill_create_ip(ip).notify_url(notifyUrl).trade_type(TradeType.JSAPI.getTradeType())
                .openid(openId).build();
        WxPayPrepayVo vo = getWxPrePayInfo(model);

        // 往 wx_预支付表添加数据
        return R.ok(vo);
    }

    private Integer changeY2F(BigDecimal realPrice) {
        return realPrice.multiply(new BigDecimal(100)).intValue();
    }

    private WxPayPrepayVo getWxPrePayInfo(UnifiedOrderModel model) {
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
        Map<String, String> params = model.createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
        String xmlResult = WxPayApi.pushOrder(false, params);

        log.info(xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String returnCode = result.get(WX_RETURN_CODE);
        String returnMsg = result.get(WX_RETURN_MSG);
        if (!WxPayKit.codeIsOk(returnCode)) {
            throw new AssertionError(returnMsg);
        }
        String resultCode = result.get(WX_RESULT_CODE);
        if (!WxPayKit.codeIsOk(resultCode)) {
            throw new AssertionError(returnMsg);
        }
        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
        String prepayId = result.get(WX_PREPAY_ID);
        Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId,
                wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        WxPayPrepayVo vo = JSONUtil.toBean(JSONUtil.toJsonStr(packageParams), WxPayPrepayVo.class);
        vo.setPrepayId(packageParams.get(WX_PACKAGE));
        return vo;
    }

    /**
     * 微信支付 异步通知
     */
    @RequestMapping(value = "/payNotify", method = {RequestMethod.POST, RequestMethod.GET})
    @Transactional(rollbackFor = Exception.class)
    public String payNotify(HttpServletRequest request) {
        String xmlMsg = HttpKit.readData(request);
        log.info("支付通知=" + xmlMsg);

        Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
        // 注意此处签名方式需与统一下单的签名类型一致(防止有人冒充微信通知）可点进去看createSign 方法，拿入参
        if (!WxPayKit.verifyNotify(params, WxPayApiConfigKit.getWxPayApiConfig().getPartnerKey(), SignType.HMACSHA256,null)) {
            log.error(WX_PAY_ERROR_PREFIX + "签名方式需与统一下单的签名类型不一致");
            return null;
        }
        if (!WxPayKit.codeIsOk(params.get(WX_RETURN_CODE))) {
            log.error(WX_PAY_ERROR_PREFIX + "签名方式一致,但微信返回状态码不正确,原始参数如下：{}", JSONUtil.toJsonStr(params));
            return null;
        }
        // 支付支付失败
        if (!WxPayKit.codeIsOk(params.get(WX_RESULT_CODE))) {
            log.error(WX_PAY_ERROR_PREFIX + "支付失败，不处理直接返回。原始参数如下：{}", JSONUtil.toJsonStr(params));
            return wxNotifyString();
        }
        String outTradeNo = params.get("out_trade_no");
        if (StrUtil.isBlank(outTradeNo)) {
            // 为null 不处理
            log.error(WX_PAY_ERROR_PREFIX + "微信回调，outTradeNo 为空。原始参数如下：{}", JSONUtil.toJsonStr(params));
            return wxNotifyString();
        }
        //2-判断是否重复通知
        if (isRepeatNotify(outTradeNo)) {
            log.info("微信重复通知，原始参数如下：{}", JSONUtil.toJsonStr(params));
            return wxNotifyString();
        }
        //正常支付成功订单

        // 通知微信 处理ok
        return wxNotifyString();
    }

    private boolean isRepeatNotify(String outTradeNo) {
        return false;
    }

    private String wxNotifyString() {
        Map<String, String> xml = new HashMap<String, String>(2);
        xml.put("return_code", "SUCCESS");
        xml.put("return_msg", "OK");
        return WxPayKit.toXml(xml);
    }
}
